#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>

#define warn(...) fprintf(stderr, __VA_ARGS__)

#ifdef __x86_64__
static int errno_by_name_x86_64(const char *errno_name)
{

#define strcase(X, N) if (!strcmp(errno_name, (X))) return N

    strcase("EPERM", 1);
    strcase("ENOENT", 2);
    strcase("ESRCH", 3);
    strcase("EINTR", 4);
    strcase("EIO", 5);
    strcase("ENXIO", 6);
    strcase("E2BIG", 7);
    strcase("ENOEXEC", 8);
    strcase("EBADF", 9);
    strcase("ECHILD", 10);
    strcase("EAGAIN", 11);
    strcase("EWOULDBLOCK", 11);
    strcase("ENOMEM", 12);
    strcase("EACCES", 13);
    strcase("EFAULT", 14);
    strcase("ENOTBLK", 15);
    strcase("EBUSY", 16);
    strcase("EEXIST", 17);
    strcase("EXDEV", 18);
    strcase("ENODEV", 19);
    strcase("ENOTDIR", 20);
    strcase("EISDIR", 21);
    strcase("EINVAL", 22);
    strcase("ENFILE", 23);
    strcase("EMFILE", 24);
    strcase("ENOTTY", 25);
    strcase("ETXTBSY", 26);
    strcase("EFBIG", 27);
    strcase("ENOSPC", 28);
    strcase("ESPIPE", 29);
    strcase("EROFS", 30);
    strcase("EMLINK", 31);
    strcase("EPIPE", 32);
    strcase("EDOM", 33);
    strcase("ERANGE", 34);
    strcase("EDEADLK", 35);
    strcase("EDEADLOCK", 35);
    strcase("ENAMETOOLONG", 36);
    strcase("ENOLCK", 37);
    strcase("ENOSYS", 38);
    strcase("ENOTEMPTY", 39);
    strcase("ELOOP", 40);
    strcase("ENOMSG", 42);
    strcase("EIDRM", 43);
    strcase("ECHRNG", 44);
    strcase("EL2NSYNC", 45);
    strcase("EL3HLT", 46);
    strcase("EL3RST", 47);
    strcase("ELNRNG", 48);
    strcase("EUNATCH", 49);
    strcase("ENOCSI", 50);
    strcase("EL2HLT", 51);
    strcase("EBADE", 52);
    strcase("EBADR", 53);
    strcase("EXFULL", 54);
    strcase("ENOANO", 55);
    strcase("EBADRQC", 56);
    strcase("EBADSLT", 57);
    strcase("EBFONT", 59);
    strcase("ENOSTR", 60);
    strcase("ENODATA", 61);
    strcase("ETIME", 62);
    strcase("ENOSR", 63);
    strcase("ENONET", 64);
    strcase("ENOPKG", 65);
    strcase("EREMOTE", 66);
    strcase("ENOLINK", 67);
    strcase("EADV", 68);
    strcase("ESRMNT", 69);
    strcase("ECOMM", 70);
    strcase("EPROTO", 71);
    strcase("EMULTIHOP", 72);
    strcase("EDOTDOT", 73);
    strcase("EBADMSG", 74);
    strcase("EOVERFLOW", 75);
    strcase("ENOTUNIQ", 76);
    strcase("EBADFD", 77);
    strcase("EREMCHG", 78);
    strcase("ELIBACC", 79);
    strcase("ELIBBAD", 80);
    strcase("ELIBSCN", 81);
    strcase("ELIBMAX", 82);
    strcase("ELIBEXEC", 83);
    strcase("EILSEQ", 84);
    strcase("ERESTART", 85);
    strcase("ESTRPIPE", 86);
    strcase("EUSERS", 87);
    strcase("ENOTSOCK", 88);
    strcase("EDESTADDRREQ", 89);
    strcase("EMSGSIZE", 90);
    strcase("EPROTOTYPE", 91);
    strcase("ENOPROTOOPT", 92);
    strcase("EPROTONOSUPPORT", 93);
    strcase("ESOCKTNOSUPPORT", 94);
    strcase("ENOTSUP", 95);
    strcase("EOPNOTSUPP", 95);
    strcase("EPFNOSUPPORT", 96);
    strcase("EAFNOSUPPORT", 97);
    strcase("EADDRINUSE", 98);
    strcase("EADDRNOTAVAIL", 99);
    strcase("ENETDOWN", 100);
    strcase("ENETUNREACH", 101);
    strcase("ENETRESET", 102);
    strcase("ECONNABORTED", 103);
    strcase("ECONNRESET", 104);
    strcase("ENOBUFS", 105);
    strcase("EISCONN", 106);
    strcase("ENOTCONN", 107);
    strcase("ESHUTDOWN", 108);
    strcase("ETOOMANYREFS", 109);
    strcase("ETIMEDOUT", 110);
    strcase("ECONNREFUSED", 111);
    strcase("EHOSTDOWN", 112);
    strcase("EHOSTUNREACH", 113);
    strcase("EALREADY", 114);
    strcase("EINPROGRESS", 115);
    strcase("ESTALE", 116);
    strcase("EUCLEAN", 117);
    strcase("ENOTNAM", 118);
    strcase("ENAVAIL", 119);
    strcase("EISNAM", 120);
    strcase("EREMOTEIO", 121);
    strcase("EDQUOT", 122);
    strcase("ENOMEDIUM", 123);
    strcase("EMEDIUMTYPE", 124);
    strcase("ECANCELED", 125);
    strcase("ENOKEY", 126);
    strcase("EKEYEXPIRED", 127);
    strcase("EKEYREVOKED", 128);
    strcase("EKEYREJECTED", 129);
    strcase("EOWNERDEAD", 130);
    strcase("ENOTRECOVERABLE", 131);
    strcase("ERFKILL", 132);
    strcase("EHWPOISON", 133);

#undef strcase

    return -1;

}
#endif

/* Try to find the errno number using the errno(1) program */
static int errno_by_name_dynamic(const char *errno_name)
{
    int i, len = strlen(errno_name);
    int err, number = -1;
    char buf[128];
    char cmd[64];
    char *end;
    long val;
    FILE *f;

    /* sanity check to not call popen with random input */
    for (i = 0; i < len; i++) {
        if (errno_name[i] < 'A' || errno_name[i] > 'Z') {
            warn("errno_name contains invalid char 0x%02x: %s\n",
                    errno_name[i], errno_name);
            return -1;
        }
    }

    snprintf(cmd, sizeof(cmd), "errno %s", errno_name);
    f = popen(cmd, "r");
    if (!f) {
        warn("popen: %s: %s\n", cmd, strerror(errno));
        return -1;
    }

    if (!fgets(buf, sizeof(buf), f)) {
        goto close;
    } else if (ferror(f)) {
        warn("fgets: %s\n", strerror(errno));
        goto close;
    }

    // expecting "<name> <number> <description>"
    if (strncmp(errno_name, buf, len) || strlen(buf) < len+2) {
        warn("expected '%s': %s\n", errno_name, buf);
        goto close;
    }
    errno = 0;
    val = strtol(buf+len+2, &end, 10);
    if (errno || end == (buf+len+2) || number < 0 || number > INT_MAX) {
        warn("can't parse the second column, expected int: %s\n", buf);
        goto close;
    }
    number = val;

close:
    err = pclose(f);
    if (err < 0)
        warn("pclose: %s\n", strerror(errno));
#ifndef __x86_64__
    /* Ignore the error for x86_64 where we have a table compiled in */
    else if (err && WEXITSTATUS(err) == 127) {
        warn("errno(1) required for errno name/number mapping\n");
    } else if (err) {
        warn("errno(1) exit status (see wait(2)): 0x%x\n", err);
    }
#endif
    return number;
}

int errno_by_name(const char *errno_name)
{
#ifdef __x86_64__
    int err;

    err = errno_by_name_x86_64(errno_name);
    if (err >= 0)
        return err;
#endif

    return errno_by_name_dynamic(errno_name);
}
